
 * 对表格的行、列进行合并 以cm开头的参数:表示按内容合并 以pm开头的参数:表示按行列号合并
 *
 * @Purpose:做一个可用于报表展示的jquery-table插件
 * @Version:1.0 08/14 2013
 * @Requires: jquery-1.7.js
 * @Author:wang yahzou QQ:506526593
 * @param {Object} $
 */

(function($) {
    // 看过jquery源码就可以发现$.fn就是$.prototype, 只是为了兼容早期版本的插件
    // 才保留了jQuery.prototype这个形式
    $.fn.mergeCell = function(options) {
        options = $.extend({
            demo : null,
            head_rows : 3,
            head_cols : 1,
            cols : 13,
            qdata : {},
            cmrows : [false, null, null], // 按内容合并的行的范围
            cmcols : [false, null, null], // 按内容合并的列的范围
            pmerges : [],
            rowwraps : null,
            colwraps : null,
            colcss : null,
            colthreds : null,
            thredcolors : [[60, 80, 90], ['#56A8F4', '#FF9937', '#CE110F']],
            evenodd : false,
            url : '',
            sum : 0,
            pagesize : 300,
            curpage : 1,
            highlight : false,
            threshold : [[60, 80, 90], ['#eee', '#ddd', '#aaa']]
        }, options || {});

        // 确保返回的仍是个jquery对象
        return this.each(function() {
            var log = log4javascript.getDefaultLogger();

            try {
                rendtoolbar($(this), options);
                init($(this), options);
                //rendheaders($(this), options);
                //cellHighLight($(this), options);
                boundEvent($(this), options);

            } catch(e) {
                inof(e);
            }

        });

    };

    // 初始化表格数据
    function init($table, options) {
        // $('tr:gt(' + Number(options.head_rows) + ')', $table).remove();
        //var log = log4javascript.getDefaultLogger();
        var data = $.extend('true', {
            pagesize : options.pagesize,
            curpage : options.curpage
        }, options.qdata);
        // log.info(data);
        if (options.url != '') {
            $.ajax({
                url : options.url,
                type : 'POST',
                dataType : "json",
                data : data,
                success : function(data) {
                    //$('#data_sum').text(data.size);
                    fillDatas($table, data.data, options);
                }
            })
        } else {
            if (options.demo != null) {
                fillDatas($table, options.demo, options);
            } else {
                rendrowcols($table, options);
            }
        }
    }

    /**
     * 加入工具条
     */
    function rendtoolbar($table, options) {
    	var date = new Date();
        var toolbar = '<tr><td colspan="' + options.cols + '" class="report_tool_bar"><ul>';
        toolbar += '<li class="report_tool_bar_l"><img src="export2.gif"/> </li></ul>';
        toolbar += '<li class="report_tool_bar_l"><a title="导出为PDF"><img src="pdf.gif"/></a></li>';
        toolbar += '<li class="report_tool_bar_l"><a title="导出为EXCEL"><img src="excel.gif"/></a></li>';
        toolbar += '<li class="report_tool_bar_r report_tool_bar_time">制表时间：2013-08-19 12:00';
        toolbar += '</td></tr>';
        $table.prepend(toolbar);
    }

    /**
     * 加入状态条
     */
    function rendStatBar($table, options) {
        var statbar = '<tr><td colspan="' + options.cols + '" class="report_tool_bar"><ul>';
        statbar += '<li class="report_tool_bar_l"><a id="button_prev" title="向前翻页">上一页</a></li></ul>';
        statbar += '<li class="report_tool_bar_l"><span>|</span></li></ul>';
        statbar += '<li class="report_tool_bar_l"><a id="button_prev" title="向前翻页">上一页</a></li></ul>';
        statbar += '<li class="report_tool_bar_r report_tool_bar_time">显示  1-' + options.sum + ' 条';
        statbar += '<li class="report_tool_bar_r report_tool_bar_time">共 ' + options.sum + ' 条数据,';
        statbar += '</td></tr>';

        $table.append(statbar);
    }

    //修改表头  表头默认会按行列合并
    function rendheaders($table, options) {
        // 改变行表头的颜色并做列合并
        $('tr:gt(0):lt(' + Number(options.head_rows) + ')', $table).each(function(index) {
            $(this).css('background', '#e8eff8');
            mergeColCell($(this), 0, options.cols);
            $('td', $(this)).css('font-weight', 'bold').css('text-align', 'center');
        });

        //对行表头做行合并
        for (var i = 0; i < options.cols; i++) {
            mergeRowCell($table, i, null, options.head_rows);
        }

        //改变列表头的颜色
        $('tr:gt(' + Number(options.head_rows) + '):lt(' + Number(options.sum) + ')', $table).each(function(index) {
            for (var i = 0; i < options.head_cols; i++) {
                $('td:eq(' + i + ')', $(this)).each(function(jndex) {
                    $(this).css('background', '#efefff').css('text-align', 'center');
                })
            }
        });

        //对列表头做行合并
        for (var i = 0; i < options.head_cols; i++) {
            mergeRowCell($table, i, options.head_rows, null);
        }
    }

    //按规则做行列合并操作
    function rendrowcols($table, options) {

        var sum = $('tr:gt(' + options.head_rows + ')', $table).size();
        options = $.extend('true', options, {
            sum : sum
        });

        var rows = options.cmrows;
        var cols = options.cmcols;
        var merges = options.pmerges;
        //合并指定列的相同内容行   优先级高
        if (cols[0]) {
            for (var i = (cols[1] || options.head_cols); i <= (cols[2] || options.cols); i++) {
                mergeRowCell($table, i, null, null);
            }
        }

        /**
         * 合并指定行的相同内容列  优先级中
         */
        if (rows[0] && !cols[0]) {
            for (var i = (rows[1] || options.head_rows); i <= (rows[2] || $('tr', $table).size()); i++) {
                mergeColCell($('tr:eq(' + i + ')', $table), options.head_cols, options.cols);
            }
        }

        /**
         * 合并指定单元格  优先级低
         */
        if (!cols[0] && !rows[0]) {
            for (var i = 0; i < merges.length; i++) {
                mergeBlock($table, merges[i][0], merges[i][1], merges[i][2], merges[i][3]);
            }
        }

        /**
         * 把某一行的td的内容用指定代码包裹
         *
         */
        if (options.rowwraps != null) {
            var rowwraps = options.rowwraps;
            for (var i = 0; i < rowwraps.length; i++) {
                $('td:gt(' + Number(options.head_cols - 1) + ')', $('tr:eq(' + rowwraps[i][0] + ')', $table)).each(function(index) {
                    $('span', $(this)).wrap(rowwraps[i][1]);
                })
            }
        }

        /**
         *
         *  把某一列td的内容用指定代码包裹
         */
        if (options.colwraps != null) {
            var colwraps = options.colwraps;
            for (var i = 0; i < colwraps.length; i++) {
                $('tr:gt(' + options.head_rows + ')', $table).each(function(index) {
                    $tr = $(this);
                    $('td:eq(' + colwraps[i][0] + ')', $tr).each(function(jndex) {
                        $('span', $(this)).wrap(colwraps[i][1]);
                    })
                })
            }
        }

        /**
         * 阀值判断并高亮显示
         */
        if (options.colthreds != null) {
            var threads = options.colthreds;
            var colors = options.thredcolors;
            for (var i = 0; i < threads.length; i++) {
                $('tr:gt(' + options.head_rows + ')', $table).each(function(index) {
                    var $tr = $(this);
                    $('td:eq(' + threads[i] + ')', $tr).each(function(jndex) {
                        var cont = $('span', $(this)).html();
                        if (!isNaN(cont)) {
                            if (Number(cont) >= colors[0][0] && Number(cont) < colors[0][1]) {
                                $(this).css('color', colors[1][0]).css('font-weight', 'bold');
                            } else if (Number(cont) >= colors[0][1] && Number(cont) < colors[0][2]) {
                                $(this).css('color', colors[1][1]).css('font-weight', 'bold');
                            } else if (Number(cont) >= colors[0][2]) {
                                $(this).css('color', colors[1][2]).css('font-weight', 'bold');
                            }
                        } else {
                        }
                    })
                })
            }
        }

        if (options.colcss != null) {
            var colcss = options.colcss;
            for (var i = 0; i < colcss.length; i++) {
                $('tr:gt(' + options.head_rows + ')', $table).each(function(index) {
                    $tr = $(this);
                    $('td:eq(' + colcss[i][0] + ')', $tr).each(function(jndex) {
                        $(this).addClass(colcss[i][1]);
                    })
                })
            }
        }

        if (options.evenodd) {
            $('tr:gt(' + options.head_rows + '):odd', $table).each(function(index) {
                $(this).css('background', '#F0F0F0');
            })
        }

        rendheaders($table, options);

        rendStatBar($table, options);

        dispose($table);
    }

    //绑定事件
    function boundEvent($table, options) {
        var log = log4javascript.getDefaultLogger();
        try {
            $('#button_prev').live('click', function() {
                if (Number(options.curpage) == 1) {
                    $('#button_prev').attr('disabled', true);
                    return false;
                }

                var cur = Number($('#current_page').text()) - 1;
                options = $.extend(true, options, {
                    curpage : cur
                });
                init($table, options);

                $('#current_page').text(cur);

                rendrowcols($table, settings);
            });
            $('#button_next').live('click', function() {
                var cur = Number($('#current_page').text()) + 1;

                if (Number(options.pagesize) * Number(options.curpage) >= Number(options.sum)) {
                    $('#button_next').attr('disabled', true);
                    return false;
                }
                options = $.extend(true, options, {
                    curpage : cur
                });
                init($table, options);
                $('#current_page').text(cur);
                rendrowcols($table, settings);
            });
            $("#button_size").live('change', function() {
                options = $.extend(true, options, {
                    pagesize : $(this).val()
                });

                init($table, options);
                rendrowcols($table, settings);
            });
        } catch (e) {
            log.info(e);
        }
    }

    // 用数据填充表格
    function fillDatas($table, datas, options) {
        var str = '';
        for (var one in datas) {
            str += '<tr>';
            for (var key in datas[one]) {
                str += '<td><span>' + datas[one][key] + '</span></td>';
            }
            str += '</tr>';
        }
        $('tbody', $table).each(function(index) {
            $(this).append(str);
        });

        rendrowcols($table, options);
    }

    //合并一个指标的区域
    function mergeBlock($table, row_s, row_e, col_s, col_e) {
        var mer_cont = '';
        var $block = $('td:eq(' + col_s + ')', $('tr:eq(' + row_s + ')', $table));

        $('tr:gt(' + Number(row_s - 1) + '):lt(' + Number(row_e - row_s + 1) + ')', $table).each(function(x_index) {
            $tr = $(this);
            $('td:gt(' + Number(col_s - 1) + '):lt(' + Number(col_e - col_s + 1) + ')', $tr).each(function(y_index) {
                if (x_index == 0 && y_index == 0) {

                } else {
                    $(this).hide();
                    // $(this).css('background', '#dddddd');
                }
            });
        });

        $block.html('');
        $block.attr('rowspan', Number(row_e - row_s + 1));
        $block.attr('colspan', Number(col_e - col_s + 1));

    }

    // 合并内容相同的列
    function mergeColCell($tr, colBegin, colEnd) {

        $tr.data('col-content-c', '');
        // 存放单元格内容
        $tr.data('col-rowspan-c', 1);
        // 存放计算的rowspan值 默认为1
        $tr.data('col-td-c', $());
        // 存放发现的第一个与前一行比较结果不同td(jQuery封装过的), 默认一个"空"的jquery对象
        //alert(colBegin + '__' + colEnd);
        //colBegin = colBegin || Number(options.head_cols - 1);
        //colEnd = colEnd || options.cols;

        $('td:gt(' + colBegin + '):lt(' + colEnd + ')', $tr).each(function(index) {
            var tdcont = $(this).html();
            if ($tr.data('col-content-c') == '') {
                $tr.data('col-content-c', tdcont);
                $tr.data('col-td-c', $(this));
            } else {
                // alert($tr.data('col-content-c')+'_'+tdcont)
                if ($tr.data('col-content-c') == tdcont) {
                    var colspan = $tr.data('col-rowspan-c') + 1;
                    $tr.data('col-rowspan-c', colspan);
                    // 值得注意的是 如果用了$td.remove()就会对其他列的处理造成影响
                    $tr.data('col-td-c').attr('colspan', $tr.data('col-rowspan-c'));
                    $(this).hide();
                } else {

                    $tr.data('col-content-c', tdcont);
                    $tr.data('col-rowspan-c', 1);
                    $tr.data('col-td-c', $(this));
                }
            }
        });
    }

    // 如果对javascript的closure和scope概念比较清楚, 这是个插件内部使用的private方法
    // 具体可以参考本人前一篇随笔里介绍的那本书
    function mergeRowCell($table, colIndex, rowBegin, rowEnd) {

        $table.data('col-content', '');
        // 存放单元格内容
        $table.data('col-rowspan', 1);

        // 存放计算的rowspan值 默认为1
        $table.data('col-td', $());
        // 存放发现的第一个与前一行比较结果不同td(jQuery封装过的), 默认一个"空"的jquery对象

        // 要处理表格的总行数, 用于最后一行做特殊处理时进行判断之用

        // 我们对每一行数据进行"扫面"处理 关键是定位col-td, 和其对应的rowspan
        rowBegin = rowBegin || 0;
        rowEnd = rowEnd || $('tr', $table).size();
        $table.data('trNum', Number(rowEnd - rowBegin - 1));
        //alert(Number(rowEnd-rowBegin));
        // alert(rowBegin + '__' + rowEnd);

        $('tr:gt(' + Number(rowBegin) + '):lt(' + Number(rowEnd + 1) + ')', $table).each(function(index) {
            // $('tr', $table).each(function(index) {
            // td:eq中的colIndex即列索引
            var $td = $('td:eq(' + colIndex + ')', this);

            // 取出单元格的当前内容
            var currentContent = $td.html();

            // 第一次时走此分支
            if ($table.data('col-content') == '') {
                $table.data('col-content', currentContent);
                $table.data('col-td', $td);

            } else {
                // 上一行与当前行内容相同
                if ($table.data('col-content') == currentContent) {
                    // 上一行与当前行内容相同则col-rowspan累加, 保存新值
                    var rowspan = $table.data('col-rowspan') + 1;
                    $table.data('col-rowspan', rowspan);
                    // 值得注意的是 如果用了$td.remove()就会对其他列的处理造成影响
                    $td.hide();

                    // 最后一行的情况比较特殊一点
                    // 比如最后2行 td中的内容是一样的,
                    // 那么到最后一行就应该把此时的col-td里保存的td设置rowspan

                    if (++index == $table.data('trNum'))
                        $table.data('col-td').attr('rowspan', $table.data('col-rowspan')).css('text-align', 'center');

                } else {// 上一行与当前行内容不同
                    // col-rowspan默认为1, 如果统计出的col-rowspan没有变化, 不处理
                    if ($table.data('col-rowspan') != 1) {
                        $table.data('col-td').attr('rowspan', $table.data('col-rowspan')).css('text-align', 'center');
                    }
                    // 保存第一次出现不同内容的td, 和其内容, 重置col-rowspan
                    $table.data('col-td', $td);
                    $table.data('col-content', $td.html());
                    $table.data('col-rowspan', 1);
                }
            }
        });
    }

    // 隔行换色
    function rendCols($table, rowBegin) {
        $('tr:gt(' + rowBegin + '):odd', $table).each(function(index) {
            $(this).css('background', '#F0F0Ff');
        });
    }

    // 同样是个private函数 清理内存之用
    function dispose($table) {
        $table.removeData();
    }

})(jQuery);
